﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Othello_Game
{
    /// <summary>
    /// The Board class is the "game logic" part of the program
    /// </summary>
    public class Board
    {
        #region private Members
        /// <summary>
        /// Number of rows on the game board
        /// </summary>
        private static int rows = 0;
        /// <summary>
        /// Number of columns on the game board
        /// </summary>
        private static int columns = 0;
        /// <summary>
        /// The game board positions
        /// </summary>
        private players[,] gameBoard;

        private List<ActionList> whatCanIDO = null;
        private players currentPlayer;
        #endregion

        #region Public Properties
        /// <summary>
        /// List of Othello_Game.ActionList the current player can play
        /// </summary>
        public List<ActionList> WhatCanIDo 
        {
            get
            {
                if (this.whatCanIDO == null)
                {
                    this.CalculateWhatCanCurrentPlayerDo();
                }

                return this.whatCanIDO;
            }
            private set
            {
                this.whatCanIDO = value;
            }
        }

        /// <summary>
        /// Gets the IPositionsValues Interface to calculate the board's position's values.
        /// </summary>
        public IPositionsValues PositionValues { get; private set; }

        /// <summary>
        /// Gets the current player on the board
        /// </summary>
        public players CurrentPlayer 
        {
            get
            {
                return this.currentPlayer;
            }
            private set
            {
                // If the value has changed
                if (value != this.currentPlayer)
                {
                    this.currentPlayer = value;

                    // Search the moves the current player can play
                    CalculateWhatCanCurrentPlayerDo();
                }
            }
        }

        #endregion

        #region Ctor
        /// <summary>
        /// Initializes a new instance of the Othello_Game.Board class
        /// </summary>
        /// <param name="rows">The number of rows on the game board</param>
        /// <param name="columns">The number of columns on the game board</param>
        public Board(int rows, int columns)
        {
            Board.rows = rows;
            Board.columns = columns;

            this.gameBoard = new players[Board.rows, Board.columns];
            this.ResetBoard();
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// checks if a given position is legal in terms of the game board size
        /// </summary>
        /// <param name="pos">The given position</param>
        /// <returns>Return true if the given position exist on the game board</returns>
        private bool CheckPosLegal(Position pos)
        {
            return ((pos.Column >= 0) && (pos.Column < columns) && (pos.Row >= 0) && (pos.Row < rows));
        }

        /// <summary>
        /// Checks if a specific position on the board is taken by some player or not
        /// </summary>
        /// <param name="pos">The given position</param>
        /// <returns>Return True if one of the players has already conquered the 
        /// given position, otherwise return false.</returns>
        private bool IsUsed(Position pos)
        {
            return !(this.gameBoard[pos.Row, pos.Column] == players.NONE);
        }

        /// <summary>
        /// returns a List of "ActionList"'s which include all possible actions for a player. 
        /// used to create the possoble actions for the computer player
        /// </summary>
        /// <returns>Returns a List of Othello_Game.ActionList which include 
        /// all possible actions for a player</returns>
        private void CalculateWhatCanCurrentPlayerDo()
        {
            this.WhatCanIDo = new List<ActionList>();
            ActionList AL;

            // Go over the entire game board
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    Position pos = new Position(i, j);

                    // Check if it can be pushed
                    if (IsLegalMove(pos))
                    {
                        AL = CalculateAllPositions(pos);

                        if (AL.Current != 0)
                        {
                            this.WhatCanIDo.Add(AL);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Performs a single Action on the board, and take over the opponents pieces
        /// start from the position the current player played.
        /// </summary>
        /// <param name="pos">The position the current player played.</param>
        /// <param name="action">The Action to perform on the board.</param>
        private void PerformAction(Position pos, Action action)
        {
            // Go to the next position on this Action Direction
            int i = pos.Row + action.Direction.Vertical;
            int j = pos.Column + action.Direction.Horizontal;

            int counter = 0;

            // Turn all the opponent pieces at the Action Direction
            while (counter < action.Ammount)
            {
                this.gameBoard[i, j] = this.CurrentPlayer;
                i += action.Direction.Vertical;
                j += action.Direction.Horizontal;
                counter++;
            }
        }

        /// <summary>
        /// First the function checks if the first step in that direction hits a position owned 
        /// by the other player if so, it checks if contiueing to "walk" in that direction 
        /// eventualy hits a position owned by the current player if it does it means that 
        /// walking in said direction yields a result (some of the opponent owned positions 
        /// will now be turned over to the current player) and the function returns the 
        /// ammount of those positions. otherwise it returns 0
        /// </summary>
        /// <param name="pos">The position played by the current player</param>
        /// <param name="direction">The direction to check</param>
        /// <returns>The number of opponent's pieces to flip at the given direction</returns>
        private int CheckDirection(Position pos, Direction direction)
        {
            int i = pos.Row;
            int j = pos.Column;
            int counter = 0;

            // While still inside the legal borders of the game board
            while (i >= 0 && i < rows && j >= 0 && j < columns)
            {
                // Advance at the stated direction 
                i += direction.Vertical;
                j += direction.Horizontal;

                // Check if the position is legal in terms of game board size
                if (CheckPosLegal(new Position(i, j)))
                {
                    // While the positions belong to the opponent, count them
                    if (gameBoard[i, j] == players.NONE)
                    {
                        if (counter == 0) return counter;

                        // Checks if current player owns the position
                        if (gameBoard[i - direction.Vertical, j - direction.Horizontal] == this.CurrentPlayer)
                        {
                            return counter;
                        }
                        else
                        {
                            return 0;
                        }
                    }
                    else if (gameBoard[i, j] == this.CurrentPlayer)
                    {
                        return counter;
                    }
                    else
                    {
                        counter++;
                    }
                }
            }
            return 0;
        }

        public int countEmptySquares()
        {
            int empty = 0;
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (!this.IsUsed(new Position(i,j)))
                    {
                        empty++;
                    }
                }
            }
            return empty;
        }

        /// <summary>
        /// Calculate for a position played by the current player all the position 
        /// on the game board taht would be affected
        /// </summary>
        /// <param name="pos">The position the current player might play</param>
        /// <returns>Return an action which contains all of the effect that 
        /// are to happen if "player" moves on "pos"</returns>
        public ActionList CalculateAllPositions(Position pos)
        {
            ActionList AL = new ActionList(pos, (Board)this.Copy());

            // Create all the directions vectors
            Direction up = new Direction(1, 0);
            Direction down = new Direction(-1, 0);
            Direction left = new Direction(0, -1);
            Direction right = new Direction(0, 1);
            Direction upLeft = new Direction(1, -1);
            Direction upRight = new Direction(1, 1);
            Direction downLeft = new Direction(-1, -1);
            Direction downRight = new Direction(-1, 1);

            int u, d, l, r, ul, ur, dl, dr;

            // Check if pieces are to be flipped at those directions
            u = CheckDirection(pos, up);
            d = CheckDirection(pos, down);
            l = CheckDirection(pos, left);
            r = CheckDirection(pos, right);
            ul = CheckDirection(pos, upLeft);
            ur = CheckDirection(pos, upRight);
            dl = CheckDirection(pos, downLeft);
            dr = CheckDirection(pos, downRight);

            // Add all the pieces flipping action to the ActionList
            if (u != 0) AL.AddToList(new Action(up, u, pos.Row, pos.Column));
            if (d != 0) AL.AddToList(new Action(down, d, pos.Row, pos.Column));
            if (l != 0) AL.AddToList(new Action(left, l, pos.Row, pos.Column));
            if (r != 0) AL.AddToList(new Action(right, r, pos.Row, pos.Column));
            if (ul != 0) AL.AddToList(new Action(upLeft, ul, pos.Row, pos.Column));
            if (ur != 0) AL.AddToList(new Action(upRight, ur, pos.Row, pos.Column));
            if (dl != 0) AL.AddToList(new Action(downLeft, dl, pos.Row, pos.Column));
            if (dr != 0) AL.AddToList(new Action(downRight, dr, pos.Row, pos.Column));

            return AL;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Counts who has more positions on the board
        /// </summary>
        /// <returns>an Othello_Game.Score</returns>
        public Score GetBoardScore()
        {
            int player1 = 0;
            int player2 = 0;

            // Go over the entire game board
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    // And count the player's pieces
                    if (this.gameBoard[i, j] == players.PLAYER1) 
                    {
                        player1++; 
                    }
                    else if (this.gameBoard[i, j] == players.PLAYER2) 
                    { 
                        player2++; 
                    }
                }
            }

            return (new Score(player1, player2));
        }

        /// <summary>
        /// Checks if one of the possible 8 positions next to the position 
        /// given as a parameter is colored (no matter what the color is)
        /// </summary>
        /// <param name="pos">A position on the game board</param>
        /// <returns>Return true if its possible to play the given position</returns>
        public bool IsLegalMove(Position pos)
        {

            if (gameBoard[pos.Row, pos.Column] == players.NONE)
            {
                Position newPos = new Position(pos.Row + 1 , pos.Column);
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Column = newPos.Column + 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Row = newPos.Row - 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Row = newPos.Row - 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Column = newPos.Column - 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Column = newPos.Column - 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Row = newPos.Row + 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

                newPos.Row = newPos.Row + 1;
                if (CheckPosLegal(newPos))
                    if (IsUsed(newPos)) return true;

            }
            return false;
        }

        /// <summary>
        /// resets the board for a new game
        /// </summary>
        public void ResetBoard()
        {
            ResetBoard(players.PLAYER1);
        }

        /// <summary>
        /// resets the board for a new game
        /// </summary>
        /// <param name="startPlayer">The player to start playing</param>
        public void ResetBoard(players startPlayer)
        {
            // Go over the entire game board
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    // set the buttons as empty
                    this.gameBoard[i, j] = players.NONE;
                }
            }

            int rowsHalf = rows / 2;
            int columnsHalf = columns / 2;

            // Set the starting position for the players
            this.gameBoard[rowsHalf, columnsHalf] = players.PLAYER2;
            this.gameBoard[rowsHalf - 1, columnsHalf - 1] = players.PLAYER2;
            this.gameBoard[rowsHalf - 1, columnsHalf] = players.PLAYER1;
            this.gameBoard[rowsHalf, columnsHalf - 1] = players.PLAYER1;

            this.whatCanIDO = null;
            this.currentPlayer = startPlayer;
        }

        /// <summary>
        /// Performs the actions in the given ActionList on the board 
        /// (explanations on the usage of Action and ActionList in the classes themselves)
        /// </summary>
        /// <param name="AL">The ActionList is the move to play on the Board instance</param>
        public void Play(ActionList AL)
        {
            // Set the current player at the ActionList's position
            this.gameBoard[AL.Pos.Row, AL.Pos.Column] = this.CurrentPlayer;

            // Go all over the available Actions in the ActionList 
            for (int i = 0; i < AL.Current; i++)
            {
                // Take over the opponent's trapped positions 
                PerformAction(AL.Pos, AL.Actions[i]);
            }
        }

        /// <summary>
        /// returns which player currently owns a specific position on the board
        /// </summary>
        /// <param name="pos">Position on the game board</param>
        /// <returns>Returns the Player who ownes the given position</returns>
        public players GetPlayer(Position pos)
        {
            return this.gameBoard[pos.Row, pos.Column];
        }

        /// <summary>
        /// Checks if a player can play
        /// </summary>
        /// <returns> Returnes true if the current player can place a piece legaly 
        /// on the game board, otherwise return false.</returns>
        public bool CanPlayerPlay()
        {
            return (WhatCanIDo.Count > 0);
        }

        /// <summary>
        /// Preforms a deep copy of the board and create a new Board Instance .
        /// </summary>
        /// <returns>A new Board instance</returns>
        public Board Copy()
        {
            Board newBoard = new Board(rows, columns);

            // Go over the entire game board
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    // Set the copied value
                    newBoard.gameBoard[i, j] = this.gameBoard[i, j];
                }
            }

            return newBoard;
        }
    
        /// <summary>
        /// Switch between the players and change the current player.
        /// Also check what moves the new player can play.
        /// </summary>
        public void SwitchPlayers()
        {
            this.CurrentPlayer = (CurrentPlayer == players.PLAYER1) ?
                                  players.PLAYER2 :
                                  players.PLAYER1;
        }

        #endregion
    }
}
